/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.filesystems;
import java.beans.*;
import java.io.*;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.TreeSet;
import java.util.Comparator;
import java.util.Iterator;
import java.util.ArrayList;
import org.openide.TopManager;
import org.openide.util.io.NbMarshalledObject;
/** This singleton object contains all {@link FileSystem}s in the IDE.
* <P>
* At any given time, no two file systems in the pool may share the same {@link FileSystem#getSystemName name}
* (unless all but one are {@link FileSystem#isValid invalid}).
*
* <p>Use {@link TopManager#getRepository} to retrieve the singleton.
*
* @author Jaroslav Tulach, Petr Hamernik
*/
public final class Repository extends Object implements org.openide.nodes.Node.Cookie, java.io.Serializable {
/** list of file systems (FileSystem)
* @associates FileSystem*/
private ArrayList fileSystems;
/** the system file system */
private FileSystem system;
/** hashtable that maps system names to FileSystems
* @associates FileSystem*/
private Hashtable names;
/** hashtable for listeners on changes in the file system.
* Its elements are of type (RepositoryListener, RepositoryListener)
* @associates RepositoryListener
*/
private Hashtable listeners = new Hashtable ();
/** default Repository implementation */
private static Repository defaultPool;
/** vetoable listener on systemName property of file system */
private VetoableChangeListener vetoListener = new VetoableChangeListener () {
/** @param ev event with changes */
public void vetoableChange (PropertyChangeEvent ev)
throws PropertyVetoException {
if (ev.getPropertyName ().equals ("systemName")) {
String nv = (String)ev.getNewValue ();
if (names.get (nv) != null) {
// changing systemName to name which is already there
String msg = ""; // TBD // NOI18N
throw new PropertyVetoException (msg, ev);
}
}
}
};
/** property listener on systemName property of file system */
private PropertyChangeListener propListener = new PropertyChangeListener () {
/** @param ev event with changes */
public void propertyChange (PropertyChangeEvent ev) {
if (ev.getPropertyName ().equals ("systemName")) {
// assign the property to new name
String ov = (String)ev.getOldValue ();
String nv = (String)ev.getNewValue ();
FileSystem fs = (FileSystem)ev.getSource ();
if (fs.isValid ()) {
// when a file system is valid then it is attached to a name
names.remove (ov);
}
// register name of the file system
names.put (nv, fs);
// the file system becomes valid
fs.setValid (true);
}
}
};
static final long serialVersionUID =-6344768369160069704L;
/** Creates new instance of file system pool and
* registers it as the default one. Also registers the default file system.
*
* @param def the default filesystem
*/
public Repository (FileSystem def) {
this.system = def;
java.net.URL.setURLStreamHandlerFactory(
new org.openide.execution.NbfsStreamHandlerFactory());
init ();
}
/** Initialazes the pool.
* @exception SecurityException if CoronaStreamHandlerFactory is already registered.
*/
private void init () {
// empties the pool
fileSystems = new ArrayList ();
names = new Hashtable ();
addFileSystem (system);
}
/** Gets the default filesystem of the IDE.
* @return the default filesystem
*/
public FileSystem getDefaultFileSystem () {
return system;
}
/** Adds new file system to the pool.
* <em>Note</em> that a file system cannot be assigned to more than one file
* system pool at one time (though currently there is only one pool anyway).
* @param fs file system to add
*/
public final void addFileSystem (FileSystem fs) {
synchronized (Repository.class) {
// if the file system is not assigned yet
if (!fs.assigned && !fileSystems.contains(fs)) {
// new file system
fileSystems.add(fs);
String systemName = fs.getSystemName ();
boolean isReg = names.get (systemName) == null;
if (isReg && !systemName.equals ("")) { // NOI18N
// file system with the same name is not there => then it is valid
names.put (systemName, fs);
fs.setValid (true);
} else {
// there is another file system with the same name => it is invalid
fs.setValid (false);
}
// mark the file system as being assigned
fs.assigned = true;
// mark as a listener on changes in the file system
fs.addPropertyChangeListener (propListener);
fs.addVetoableChangeListener (vetoListener);
// fire info about new file system
fireFileSystem (fs, true);
}
}
}
/** Removes a file system from the pool.
* @param fs file system to remove
*/
public final void removeFileSystem (FileSystem fs) {
synchronized (Repository.class) {
if (fs.isDefault()) return;
if (fileSystems.remove(fs)) {
// the file system realy was here
if (fs.isValid ()) {
// if the file system is valid then is in names hashtable
names.remove (fs.getSystemName ());
}
// in all cases remove it from listeners
fs.removePropertyChangeListener (propListener);
fs.removeVetoableChangeListener (vetoListener);
fireFileSystem (fs, false);
}
// unassign the file system
fs.assigned = false;
}
}
/** Reorders {@link FileSystem}s by given permutation.
* For example, if there are three file systems, <code>new int[] {2, 0, 1}</code> cycles the file systems forwards.
* @param perm an array of integers
* @throws IllegalArgumentException if the array is not a permutation, or is not the same length as the current number of file systems in the pool
*/
public final void reorder(int[] perm) {
synchronized (getClass()) {
if (perm == null || perm.length != fileSystems.size() || !isPermutation(perm))
throw new IllegalArgumentException();
ArrayList newList = new ArrayList (fileSystems.size ());
int len = perm.length;
for (int i = 0; i < len; i++) {
newList.add (fileSystems.get (perm[i]));
}
fileSystems = newList;
fireFileSystemReordered(perm);
}
}
/** @return true if the parameter describes a permutation */
private static boolean isPermutation(int[] perm) {
final int len = perm.length;
boolean[] bool = new boolean[len];
try {
for (int i = 0; i < len; i++) {
if (bool[perm[i]]) return false;
else bool[perm[i]] = true;
}
return true;
} catch (IndexOutOfBoundsException e) {
return false;
}
}
/** Returns enumeration of all file systems.
* @return enumeration of type {@link FileSystem}
*/
public final Enumeration getFileSystems () {
return java.util.Collections.enumeration ((java.util.Collection)fileSystems.clone());
}
/** Returns enumeration of all file systems.
* @return enumeration of type {@link FileSystem}
*/
public final Enumeration fileSystems () {
return getFileSystems ();
}
/** Returns a sorted array of file systems. */
public final FileSystem[] toArray() {
synchronized (getClass()) {
FileSystem[] fss = new FileSystem[fileSystems.size()];
fileSystems.toArray(fss);
return fss;
}
}
/** Finds file system when only its system name is known.
* @param systemName {@link FileSystem#getSystemName name} of the file system
* @return the file system or <CODE>null</CODE> if there is no such
* file system
*/
public final FileSystem findFileSystem (String systemName) {
FileSystem fs = (FileSystem)names.get (systemName);
return fs;
}
/** Saves pool to stream by saving all file systems.
* The default (system) file system, or any non-persistent file systems, are skipped.
*
* @param oos object output stream
* @exception IOException if an error occures
*/
public void writeExternal (ObjectOutput oos) throws IOException {
synchronized (getClass()) {
Iterator iter = fileSystems.iterator();
while (iter.hasNext()) {
FileSystem fs = (FileSystem)iter.next();
if (!fs.isDefault () && !fs.isPersistent ()) {
oos.writeObject (new NbMarshalledObject (fs));
}
}
oos.writeObject (null);
}
}
/** Reads object from stream.
* Reads all filesystems. Persistent and system file systems are untouched; all others are removed and possibly reread.
* @param oos object output stream
* @exception IOException if an error occures
* @exception ClassNotFoundException if read class is not found
*/
public void readExternal (ObjectInput ois)
throws IOException, ClassNotFoundException {
ArrayList temp = new ArrayList(10);
synchronized (Repository.class) {
for (;;) {
Object obj = ois.readObject ();
if (obj == null) {
// all system has been read in
break;
}
FileSystem fs;
if (obj instanceof FileSystem) {
fs = (FileSystem)obj;
} else {
try {
NbMarshalledObject mar = (NbMarshalledObject)obj;
fs = (FileSystem)mar.get ();
} catch (IOException ex) {
TopManager.getDefault().notifyException(ex);
fs = null;
} catch (ClassNotFoundException ex) {
TopManager.getDefault().notifyException(ex);
fs = null;
}
}
if (fs != null) {
// add the new file system
temp.add(fs);
}
}
Enumeration ee = getFileSystems();
FileSystem fs;
while (ee.hasMoreElements()) {
fs = (FileSystem) ee.nextElement();
if (!fs.isPersistent ()) {
removeFileSystem (fs);
}
}
// in init assigned is checked and we force 'system' to be added again
system.assigned = false;
init ();
// all is successfuly read
for (Iterator iter = temp.iterator(); iter.hasNext();)
addFileSystem ((FileSystem) iter.next());
}
}
/** Finds file when its name is provided. It scans in the list of
* file systems and asks them for the specified file by a call to
* {@link FileSystem#find find}. The first object that is found is returned or <CODE>null</CODE>
* if none of the file systems contain such a file.
*
* @param aPackage package name where each package is separated by a dot
* @param name name of the file (without dots) or <CODE>null</CODE> if
* one wants to obtain the name of a package and not a file in it
* @param ext extension of the file or <CODE>null</CODE> if one needs
* a package and not a file name
*
* @return {@link FileObject} that represents file with given name or
* <CODE>null</CODE> if the file does not exist
*/
public final FileObject find (String aPackage, String name, String ext) {
Enumeration en = getFileSystems ();
while (en.hasMoreElements ()) {
FileSystem fs = (FileSystem)en.nextElement ();
FileObject fo = fs.find (aPackage, name, ext);
if (fo != null) {
// object found
return fo;
}
}
return null;
}
/** Searches for the given resource among all file systems.
* @see FileSystem#findResource
* @param name a name of the resource
* @return file object or <code>null</code> if the resource can not be found
*/
public FileObject findResource(String name) {
Enumeration en = getFileSystems ();
while (en.hasMoreElements ()) {
FileSystem fs = (FileSystem)en.nextElement ();
FileObject fo = fs.findResource(name);
if (fo != null) {
// object found
return fo;
}
}
return null;
}
/** Searches for the given resource among all file systems, returning all matches.
* @param name name of the resource
* @return enumeration of {@link FileObject}s
*/
public Enumeration findAllResources(String name) {
Vector v = new Vector(8);
Enumeration en = getFileSystems ();
while (en.hasMoreElements ()) {
FileSystem fs = (FileSystem)en.nextElement ();
FileObject fo = fs.findResource(name);
if (fo != null) {
v.addElement(fo);
}
}
return v.elements();
}
/** Finds all files among all file systems matching a given name, returning all matches.
* All file systems are queried with {@link FileSystem#find}.
*
* @param aPackage package name where each package is separated by a dot
* @param name name of the file (without dots) or <CODE>null</CODE> if
* one wants to obtain the name of a package and not a file in it
* @param ext extension of the file or <CODE>null</CODE> if one needs
* a package and not a file name
*
* @return enumeration of {@link FileObject}s
*/
public final Enumeration findAll (String aPackage, String name, String ext) {
Enumeration en = getFileSystems ();
Vector ret = new Vector();
while (en.hasMoreElements ()) {
FileSystem fs = (FileSystem)en.nextElement ();
FileObject fo = fs.find (aPackage, name, ext);
if (fo != null) {
ret.addElement(fo);
}
}
return ret.elements();
}
/** Fire info about changes in the file system pool.
* @param fs file system
* @param add <CODE>true</CODE> if the file system is added,
* <CODE>false</CODE> if it is removed
*/
private void fireFileSystem (FileSystem fs, boolean add) {
Enumeration en = ((Hashtable)listeners.clone ()).elements ();
RepositoryEvent ev = new RepositoryEvent (this, fs, add);
while (en.hasMoreElements ()) {
RepositoryListener list = (RepositoryListener)en.nextElement ();
if (add) {
list.fileSystemAdded (ev);
} else {
list.fileSystemRemoved (ev);
}
}
}
/** Fires info about reodering
* @param perm
*/
private void fireFileSystemReordered(int[] perm) {
Enumeration en = ((Hashtable)listeners.clone ()).elements ();
RepositoryReorderedEvent ev = new RepositoryReorderedEvent(this, perm);
while (en.hasMoreElements ()) {
RepositoryListener list = (RepositoryListener)en.nextElement ();
list.fileSystemPoolReordered(ev);
}
}
/** Adds new listener.
* @param list the listener
*/
public void addRepositoryListener (RepositoryListener list) {
listeners.put (list, list);
}
/** Removes listener.
* @param list the listener
*/
public void removeRepositoryListener (RepositoryListener list) {
listeners.remove (list);
}
/** Writes the object to the stream.
*/
private Object writeReplace () {
return new java.io.Serializable () {
/** serial version UID */
static final long serialVersionUID=-3874531277726540941L;
private void writeObject (ObjectOutputStream oos) throws IOException {
TopManager.getDefault ().getRepository ().writeExternal (oos);
}
private void readObject (ObjectInputStream ois)
throws IOException, ClassNotFoundException {
TopManager.getDefault ().getRepository ().readExternal (ois);
}
/** @return the default pool */
public Object readResolve () {
return TopManager.getDefault ().getRepository ();
}
};
}
}
/*
* Log
* 13 Gandalf 1.12 1/13/00 Ian Formanek NOI18N
* 12 Gandalf 1.11 1/12/00 Ian Formanek NOI18N
* 11 Gandalf 1.10 11/29/99 Petr Kuzel Repository node tagged
* by Repository cookie.
* 10 Gandalf 1.9 11/25/99 Jaroslav Tulach Safer serialization.
* 9 Gandalf 1.8 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 8 Gandalf 1.7 8/9/99 Ian Formanek Generated Serial Version
* UID
* 7 Gandalf 1.6 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 6 Gandalf 1.5 6/7/99 Jaroslav Tulach FS capabilities.
* 5 Gandalf 1.4 3/26/99 Jaroslav Tulach
* 4 Gandalf 1.3 3/21/99 Jaroslav Tulach Repository displayed ok.
* 3 Gandalf 1.2 3/19/99 Jaroslav Tulach Serial version UID
* 2 Gandalf 1.1 3/19/99 Jaroslav Tulach TopManager.getDefault
* ().getRegistry ()
* 1 Gandalf 1.0 2/11/99 Ian Formanek
* $
* Beta Change History:
*/